using System;
using System.Collections.Generic;
using System.Threading;

namespace Dida
{
	public delegate void Task();

	public interface ITaskQueue
	{
		void Add(Task task);
	}

	public class ImmediateTaskQueue : ITaskQueue
	{
		public void Add(Task task)
		{
			task();
		}
	}

	public class ThreadedTaskQueue : ITaskQueue
	{
		InterlockedQueue<Task> taskQueue = new InterlockedQueue<Task>();

		public void Add(Task task)
		{
			taskQueue.Push(task);
		}

		/// <summary>
		/// Runs the next task in the task queue. If no tasks are available, 
		/// nothing will be done.
		/// </summary>
		/// <returns>true if a task was run, false if no task was available.</returns>
		/// <seealso cref="TasksPending"/>
		public bool RunNextTask()
		{
			Task taskToRun;
			
			taskToRun = taskQueue.Pop();
			if (taskToRun != null)
			{
				taskToRun();
				return true;
			}
			else
				return false;
		}

		public void Flush()
		{
			while (true)
			{
				Task task = taskQueue.Pop();
				if (task != null)
					task();
				else
					break;
			}
		}

		/// <summary>
		/// Gets whether there are any tasks pending to be run in this task queue.
		/// </summary>
		public bool TasksPending
		{
			get
			{
				return taskQueue.Peek() != null;
			}
		}
	}

	public class TaskThread : ThreadedTaskQueue
	{
		Thread thread;
		bool running = false;

		public TaskThread()
		{
		}

		public void Start()
		{
			thread = new Thread(ThreadStart);
			thread.IsBackground = true;

			running = true;
			if (!thread.IsAlive)	
				thread.Start();
		}

		private void ThreadStart()
		{
			while (running)
			{
				if (!RunNextTask())
				{
					// don't eat processor time when there are no tasks pending.
					Thread.Sleep(0);
				}

				// to make sure we have an up-to-date running flag.
				Thread.MemoryBarrier();
			}
		}

		public void Stop()
		{
			running = false;
		}
	}
}
